package edu.stu.bitmapcolorchange.utils

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.graphics.Paint
import androidx.annotation.DrawableRes
import kotlin.math.max
import kotlin.math.min


object BitmapUtil {

    fun convertResourceToBitmap(context: Context, @DrawableRes resId: Int): Bitmap =
        BitmapFactory.decodeResource(context.resources, resId)

    /**
     * Bitmap2gray bitmap
     *
     * @param bmSrc 源彩色图像
     * @return 转换后的灰度图像
     */
    fun bitmap2GrayBitmap(bmSrc: Bitmap): Bitmap {
        // 得到图片的长和宽
        val width = bmSrc.getWidth()
        val height = bmSrc.getHeight()

        // 创建目标灰度图像
        val bmpGray = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
        // 创建画布
        val canvas = Canvas(bmpGray)
        val paint = Paint()
        // 创建一个 ColorMatrix 对象 cm，用于处理颜色矩阵
        val cm = ColorMatrix()
        // 调用 ColorMatrix 的 setSaturation() 方法，将饱和度设置为 0，表示将颜色饱和度降为 0，即转换为灰度图像
        cm.setSaturation(0f)
        // 创建一个 ColorMatrixColorFilter 对象 f，用于应用颜色矩阵到画笔的颜色过滤器
        val f = ColorMatrixColorFilter(cm)
        // 将创建的颜色过滤器 f 应用到画笔 paint 中，以便在绘制 Bitmap 时应用灰度效果
        paint.setColorFilter(f)

        // 将源图像绘制到画布上并应用灰度效果
        canvas.drawBitmap(bmSrc, 0f, 0f, paint)
        return bmpGray
    }

    /**
     * Bitmap2light bitmap 将 Bitmap 转换为高亮效果的 Bitmap
     *
     * @param bitmap bitmap 原始 Bitmap 对象
     * @return 处理后的 Bitmap 对象，高亮效果
     */
    fun bitmap2LightBitmap(bitmap: Bitmap): Bitmap {
        // 得到图片的长和宽
        val width = bitmap.getWidth()
        val height = bitmap.getHeight()

        // 创建目标 Bitmap 对象，使用 ARGB_8888 格式，支持透明度
        val bmpGray = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)

        // 设置颜色矩阵，控制图片的高亮效果
        val a = FloatArray(20)
        a[0] = 1.also {
            a[18] = it.toFloat()
        }.also {
            a[12] = it.toFloat()
        }.also {
            a[6] = it.toFloat()
        }.toFloat()

        a[4] = 50.also {
            a[14] = it.toFloat()
        }.also {
            a[9] = it.toFloat()
        }.toFloat()

        val cm = ColorMatrix(a)
        val paint = Paint()
        val f = ColorMatrixColorFilter(cm)
        paint.setColorFilter(f)

        val c = Canvas(bmpGray)
        c.drawBitmap(bitmap, 0f, 0f, paint)
        return bmpGray
    }

    /**
     * Gray bitmap2binary bitmap 将灰度图像转换为二值化图像
     *
     * @param grayMap 输入的灰度图像
     * @param isReverse 是否反转黑白颜色，true表示黑色代表前景（目标），白色代表背景；false表示白色代表前景，黑色代表背景
     * @return  转换后的二值化图像
     */
    fun grayBitmap2BinaryBitmap(grayMap: Bitmap, isReverse: Boolean): Bitmap {
        // 得到图片的长和宽
        val width = grayMap.getWidth()
        val height = grayMap.getHeight()

        // 创建二值化图像
        val binaryMap = grayMap.copy(Bitmap.Config.ARGB_8888, true)
        // 依次循环，对图像的像素进行处理
        for (i in 0 until width) {
            for (j in 0 until height) {
                // 获取当前像素的值
                val col = binaryMap.getPixel(i, j)
                // 得到 alpha通道的值
                val alpha = Color.alpha(col)
                // 得到红色通道的值
                val red = Color.red(col)
                // 得到绿色通道的值
                val green = Color.green(col)
                // 得到蓝色通道的值
                val blue = Color.blue(col)

                /**
                 * 计算灰度值，使用加权公式：
                 * Gray = (R * 38 + G * 75 + B * 15) >> 7
                 * 其中，38、75 和 15 是 RGB 通道的权重
                 */
                var gray = red * 38 + green * 75 + blue * 15 shr 7

                // 将灰度值限制在 0 到 255 之间
                gray = max(0, min(gray, 255))

                // 根据灰度值和阈值进行二值化处理
                val threshold = 128 // 设定的阈值
                val binaryColor = if (gray > threshold) {
                    // 如果灰度值大于阈值，根据是否反转选择黑色或白色
                    if (isReverse) Color.BLACK else Color.WHITE
                } else {
                    // 如果灰度值小于等于阈值，根据是否反转选择白色或黑色
                    if (isReverse) Color.WHITE else Color.BLACK
                }

                //设置新图像的当前像素值
                binaryMap.setPixel(i, j, binaryColor);
            }
        }
        return binaryMap;
    }

    /**
     * Gray average bitmap2binary bitmap  将灰度均值化的图像平均转换为二值化图像
     *
     * @param srcBitmap 输入的灰度均值化图像
     * @return 转换后的二值化图像，如果输入图像为空则返回 null
     */
    fun grayAverageBitmap2BinaryBitmap(srcBitmap: Bitmap): Bitmap? {
        val width = srcBitmap.getWidth()
        val height = srcBitmap.getHeight()

        val pixelTotal = width * height
        if (pixelTotal == 0) {
            return null
        }

        val bitmap = srcBitmap.copy(Bitmap.Config.ARGB_8888, true)

        // 总灰度
        var sum = 0
        // 阈值
        var threshold = 0

        for (i in 0 until pixelTotal) {

            val x = i % width
            val y = i / width

            val pixel = bitmap.getPixel(x, y)

            val alpha = Color.alpha(pixel)
            val red = Color.red(pixel)
            val green = Color.green(pixel)
            val blue = Color.blue(pixel)

            var gray = red * 38 + green * 75 + blue * 15 shr 7

            if (alpha == 0 && gray == 0) {
                gray = 0xFF
            }

            if (gray > 0XFF) {
                gray = 0xFF
            }

            bitmap.setPixel(x, y, gray or -0x100)

            sum += gray
        }
        // 计算平均灰度
        threshold = sum / pixelTotal

        for (i in 0 until pixelTotal) {
            val x = i % width
            val y = i / width
            val pixel = bitmap.getPixel(x, y) and 0x000000FF
            val color = if (pixel <= threshold) Color.BLACK else Color.WHITE
            bitmap.setPixel(x, y, color);
        }
        return bitmap
    }


    fun bitmap2OTSUBitmap(srcBitmap: Bitmap): Bitmap? {

        val width = srcBitmap.getWidth()
        val height = srcBitmap.getHeight()

        val pixelTotal = width * height
        if (pixelTotal == 0) {
            return null
        }

        val bitmap = srcBitmap.copy(Bitmap.Config.ARGB_8888, true)

        // 总灰度值
        var sum1 = 0
        // 背景总灰度值
        var sumB:Long = 0
        // 背景像素点比例
        var wB = 0.0
        // 前景像素点比例
        var wF = 0.0
        // 背景平均灰度值
        var mB = 0.0
        // 前景平均灰度值
        var mF = 0.0
        // 最大类间方差
        var maxG = 0.0
        // 类间方差
        var g = 0.0
        // 阈值
        var threshold = 0

        // 灰度直方图，下标是灰度值，保存内容是灰度值对应的像素点总数
        val histogram = DoubleArray(256)

        // 获取灰度直方图和总灰度
        for (i in 0 until pixelTotal) {
            val x = i % width
            val y = i / width
            val pixel = bitmap.getPixel(x, y)

            // 分离三原色及透明度
            val alpha = Color.alpha(pixel)
            val red = Color.red(pixel)
            val green = Color.green(pixel)
            val blue = Color.blue(pixel)

            var gray = red * 38 + green * 75 + blue * 15 shr 7

            if (alpha == 0 && gray == 0) {
                gray = 0xFF
            }

            if (gray > 0XFF) {
                gray = 0xFF
            }

            bitmap.setPixel(x, y, gray or -0x100)

            // 计算灰度直方图分布，Histogram 数组下标是灰度值，保存内容是灰度值对应像素点数
            histogram[gray]++
            sum1 += gray
        }
        // OTSU 算法
        for (i in 0 until 256) {
            // 这里不算比例，减少运算，不会影响求 T
            wB += histogram[i]
            wF = pixelTotal - wB
            if (wB == 0.0 || wF == 0.0) {
                continue
            }
            sumB = (sumB + i * histogram[i]).toLong()
            mB = sumB / wB
            mF = (sum1 - sumB) / wF
            g = wB * wF * (mB - mF) * (mB - mF)
            if (g >= maxG) {
                threshold = i;
                maxG = g;
            }
        }
        for (i in 0 until pixelTotal) {
            val x = i % width
            val y = i / width
            val pixel = bitmap.getPixel(x, y) and 0x000000FF
            val color = if (pixel <= threshold) Color.BLACK else Color.WHITE
            bitmap.setPixel(x, y, color);
        }
        return bitmap
    }
}